//------------------------------------------------------------------------------
// The contents of this file are subject to the Microsoft Public License (Ms-PL)
// You may obtain a copy of the License at http://psadmin.codeplex.com/license. 
// 
// Software distributed under the License is distributed on an "AS IS" basis, 
// WITHOUT WARRANTY OF ANY KIND, either express or implied. 
// See the License for the specific language governing rights and limitations 
// under the License.
// 
// The initial developer is Ben Foster (ben.foster@retroviz.com)
// Copyright (c) Retroviz Limited (http://www.retroviz.com).
// All Rights Reserved.
//------------------------------------------------------------------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using PSAdmin.Core;
using PSAdmin.Core.Domain;
using PSAdmin.Core.Interfaces;
using PSAdmin.Core.Utils;
using PSAdmin.Web.Infrastructure;
using PSAdmin.Web.Models;

namespace PSAdmin.Web.Controllers
{   
    public class ScriptController : SiteController
    {
        int _pageSize = 6;
        
        IPSScriptRepository _scriptRepository;
        IUserRoleRepository _roleRepository;
        IUserEventRepository _eventRepository;
        IScriptService _scriptService;

        public ScriptController(IScriptService scriptService, IPSScriptRepository scriptRepository, IUserRoleRepository roleRepository,
            IUserEventRepository eventRepository, ISessionStore sessionStore) : base(sessionStore) {
            _scriptService = scriptService;
            _scriptRepository = scriptRepository;
            _roleRepository = roleRepository;
            _eventRepository = eventRepository;
        }
        
        //
        // GET: /Script/

        [HttpGet]
        public ActionResult Index() {
            ViewData["console"] = this.CurrentConsole;
            return View();
        }

        //
        // GET: /Script/List/Page2

        [HttpGet]
        public ActionResult List(int? page) {
            var pageIndex = page ?? 1;
            
            var scripts = (this.IsAdminRequest()) 
                ? _scriptRepository.GetPaged(pageIndex, _pageSize)
                :_scriptRepository.GetScriptsForUser(User.Identity.Name, pageIndex, _pageSize);

            return View(scripts);
        }

        // 
        // GET: /Script/Details/1234

        [HttpGet]
        public ActionResult Details(int? id) {
            
            if (!id.HasValue) return RedirectToAction("List");

            var script = _scriptRepository.GetById(id.Value);
            
            if (script == null) return RedirectToAction("List");    
            
            if (!this.CanExecuteScript(script, _roleRepository.GetUserRoles(User.Identity.Name)))
                return RedirectToAction("UnAuthorized");

            return View(script);
        }

        //
        // POST: /Script/Details/1234?...

        [HttpPost]
        public ActionResult Details(int id, IDictionary<int, string> variables) {
            var script = _scriptRepository.GetById(id);
            
            if (script == null)
                return RedirectToAction("Scripts");

            var data = new Dictionary<string, object>();

            if (script.HasVariables && variables != null) {
                foreach (var v in variables) {
                    var scriptVariable = script.Variables.FirstOrDefault(x => x.Id == v.Key);
                    
                    if (scriptVariable == null)
                        throw new InvalidOperationException("Script variable does not exist.");

                    if (string.IsNullOrEmpty(v.Value)) {
                        ModelState.AddModelError("", string.Format("A value has not been entered for variable {0}",
                            scriptVariable.Name));
                    }
                }

                if (!ModelState.IsValid)
                    return View(script);

                foreach (var variable in script.Variables) {
                    if (variables.ContainsKey(variable.Id)) {
                        data.Add(variable.Token, variables[variable.Id]);
                    }
                }
            }

            var result = _scriptService.ExecuteScript(script, data);

            this.CurrentConsole += result;

            // audit
            var userEvent = this.CreateEvent(script.Id, UserBehaviour.ExecuteScript, script.Name,
                data.ConvertToString(), string.Empty); // TODO need to store output efficiently

            _eventRepository.Save(userEvent);

            return RedirectToAction("Index");
        }

        //
        // GET: /Script/Create

        [HttpGet]
        [Authorize(Roles="Administrators")]
        public ActionResult Create() {
            return View();
        }

        // POST: /Script/Create

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult Create([Bind(Exclude="Id")]PSScript script) {
            if (ModelState.IsValid) {
                var result = SaveScript(script);
                return RedirectToAction("Edit", new { id = result });
            }
            return View();
        }

        //
        // GET: /Script/Edit/1234

        [HttpGet]
        [Authorize(Roles = "Administrators")]
        public ActionResult Edit(int? id)
        {           
            if (!id.HasValue) return RedirectToAction("List");

            var script = _scriptRepository.GetById(id.Value);

            if (script == null) return RedirectToAction("List");

            var roles = _roleRepository.GetAll();
            var editScript = new PSScriptEditViewModel(script, roles);

            return View(editScript);
        }

        //
        // POST: /Script/Edit/

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult Edit([Bind(Exclude = "Roles, Variables")]PSScript script, Guid[] scriptRoles)
        {                      
            if (ModelState.IsValid) {

                if (scriptRoles != null) {
                    script.Roles.Clear(); // not the most efficient way, but it works
                    foreach (var roleId in scriptRoles) {
                        var role = _roleRepository.GetById(roleId);
                        script.Roles.Add(role);
                    }
                }
                SaveScript(script);
            }

            return RedirectToAction("Edit", new { id = script.Id});
        }

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult Delete(int id) {
            var script = _scriptRepository.GetById(id);
            if (script != null)
                _scriptRepository.Delete(script);

            return RedirectToAction("List");
        }

        //
        // POST: /Script/GetVariable/12345?scriptId=12

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult GetVariable(int id, int scriptId) {
            var script = _scriptRepository.GetById(scriptId);

            var variable = script.Variables.FirstOrDefault(x => x.Id == id);

            return PartialView("VariableDetails", variable);
        }

        //
        // POST: /Script/SaveVariable/

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult SaveVariable(PSScriptVariable variable)
        {
            var script = _scriptRepository.GetById(variable.Script.Id);
            var v = script.Variables.FirstOrDefault(x => x.Id == variable.Id);

            v.Name = variable.Name;
            v.Description = variable.Description;
            v.Token = variable.Token;

            _scriptRepository.Save(script);

            return RedirectToAction("Edit", new { id = variable.Script.Id });
        }

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult CreateVariable(int scriptId, FormCollection values)
        {
            var script = _scriptRepository.GetById(scriptId);

            var variable = new PSScriptVariable() {
                Name = values.Get("NewVariableName"),
                Description = values.Get("NewVariableDescription"),
                Token = values.Get("NewVariableToken")
            };

            script.AddScriptVariable(variable);
            _scriptRepository.Save(script);

            return RedirectToAction("Edit", new { id = script.Id });
        }

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public JsonResult DeleteVariable(int id, int scriptId)
        {
            bool result = false;
            var script = _scriptRepository.GetById(scriptId);

            if (script != null)
            {
                var variable = script.Variables.FirstOrDefault(x => x.Id == id);
                if (variable != null)
                {
                    script.Variables.Remove(variable);
                    _scriptRepository.Save(script);
                    result = true;
                }                  
            }
            return Json(result);
        }

        [HttpPost]
        [Authorize(Roles = "Administrators")]
        public ActionResult Execute(string submitButton, string command)
        {
            if (submitButton == "Clear") {
                this.CurrentConsole = string.Empty;
            }

            // not implemented
            if (submitButton == "Execute" && !string.IsNullOrEmpty(command)) {
                var script = new PSScript() {
                    Name = "Ad hoc script",
                    CommandText = command
                };

                this.CurrentConsole += _scriptService.ExecuteScript(script, new Dictionary<string, object>());
            }
            if (Request.IsAjaxRequest())
            {
                return Content(this.CurrentConsole, "text/plain");
            }
            return RedirectToAction("Index");
        }

        [HttpGet]
        public ActionResult Download()
        {
            if (!string.IsNullOrEmpty(this.CurrentConsole)) {
                var consoleBytes = System.Text.Encoding.UTF8.GetBytes(this.CurrentConsole);
                return File(consoleBytes, "text/plain", "PSAdminSession.txt");
            }
            return RedirectToAction("Index");
        }

        [HttpGet]
        public ActionResult Unauthorized() {
            return View();
        }

        [NonAction]
        private int SaveScript(PSScript script) {
            if (!string.IsNullOrEmpty(script.Password))
                script.Password = EncryptionHelper.Encrypt(script.Password);
            return _scriptRepository.Save(script);
        }
    }
}
